
type register = 
(* argument 
	Nous les utiliserons pour effectuer nos calculs *)
  | A0 | A1 | A2 | A3 

(* temporary (not preserved across call) 
	Nous les utiliserons pour stocker les valeurs des Variables & Durant les calcul dans vars*)
  | T0 | T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9

(*saved temporary (preserved across call)*)
  | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 	

(*expression evaluation and results of a function*)
  | V0 | V1 

  | RA (*eturn address (used by function call)*)
  | SP (* stack pointer *)
  | FP (* frame pointer *)

type address =
  | Alab of string
  | Areg of int * register

type operand =
  | Oimm of int
	  | Oreg of register

type arith = Add | Sub | Mul | Div

type instruction =
  | Move of register * register
  | Li of register * int
  | La of register * string
  | Lw of register * address
  | Sw of register * address
  | Arith of arith * register * register * operand
  | Jal of string
  | Jr of register
  | Syscall
  | Label of string
  | NOP

type data = 
  | Asciiz of string * string
  | Word of string * int

type program = {
  text : instruction list;
  data : data list;
}

open Format

let print_register fmt = function
  | A0 -> pp_print_string fmt "$a0"
  | A1 -> pp_print_string fmt "$a1"
  | A2 -> pp_print_string fmt "$a2"
  | A3 -> pp_print_string fmt "$a3"
  | T0 -> pp_print_string fmt "$t0"
  | T1 -> pp_print_string fmt "$t1"
  | T2 -> pp_print_string fmt "$t2"
  | T3 -> pp_print_string fmt "$t3"
  | T4 -> pp_print_string fmt "$t4"
  | T5 -> pp_print_string fmt "$t5"
  | T6 -> pp_print_string fmt "$t6"
  | T7 -> pp_print_string fmt "$t7"
  | T8 -> pp_print_string fmt "$t8"
  | T9 -> pp_print_string fmt "$t9"
  | S0 -> pp_print_string fmt "$s0"
  | S1 -> pp_print_string fmt "$s1"
  | S2 -> pp_print_string fmt "$s2"
  | S3 -> pp_print_string fmt "$s3"
  | S4 -> pp_print_string fmt "$s4"
  | S5 -> pp_print_string fmt "$s5"
  | S6 -> pp_print_string fmt "$s6"
  | S7 -> pp_print_string fmt "$s7"
  | V0 -> pp_print_string fmt "$v0"
  | V1 -> pp_print_string fmt "$v1"
  | RA -> pp_print_string fmt "$ra"
  | SP -> pp_print_string fmt "$sp"
  | FP -> pp_print_string fmt "$fp"

let print_arith fmt = function
  | Add -> pp_print_string fmt "add"
  | Sub -> pp_print_string fmt "sub"
  | Mul -> pp_print_string fmt "mul"
  | Div -> pp_print_string fmt "div"

let print_address fmt = function
  | Alab s -> pp_print_string fmt s
  | Areg (ofs, r) -> fprintf fmt "%d(%a)" ofs print_register r

let print_operand fmt = function
  | Oimm i -> pp_print_int fmt i
  | Oreg r -> print_register fmt r

let print_instruction fmt = function
  | Move (dst, src) -> 
      fprintf fmt "\tmove %a, %a\n" print_register dst print_register src
  | Li (r, i) ->
      fprintf fmt "\tli   %a, %d\n" print_register r i
  | La (r, s) ->
      fprintf fmt "\tla   %a, %s\n" print_register r s
  | Lw (r, a) ->
      fprintf fmt "\tlw   %a, %a\n" print_register r print_address a
  | Sw (r, a) ->
      fprintf fmt "\tsw   %a, %a\n" print_register r print_address a
  | Arith (a, dst, src, op) ->
      fprintf fmt "\t%a  %a, %a, %a\n" 
	print_arith a print_register dst print_register src print_operand op
  | Jal s ->
      fprintf fmt "\tjal  %s\n" s
  | Jr r ->
      fprintf fmt "\tjr   %a\n" print_register r
  | Syscall ->
      fprintf fmt "\tsyscall\n"
  | Label s ->
      fprintf fmt "%s:" s
  | _ -> ()

let print_data fmt = function
  | Asciiz (l, s) -> 
      fprintf fmt "%s:\t.asciiz %S\n" l s
  | Word (l, n) ->
      fprintf fmt "%s:\t.word %d\n" l n

let print_program fmt p =
  fprintf fmt "\t.text\n";
  List.iter (print_instruction fmt) p.text;
  fprintf fmt "\t.data\n";
  List.iter (print_data fmt) p.data

